home *** CD-ROM | disk | FTP | other *** search
- /* Use this file to write patches to traps. You can create as many
- patches as you like. Each patch is created as a non-relocatable block in
- the heap. All patches are automatically removed when ExitToShell is
- called. Removing all patches before the application exits doesn't seem
- to be necessary with MultiFinder, but under Finder you must remove
- the patches before exiting.
-
- You don't have to setup register A5 (or A4 for code resources)
- since the patch glue will already have taken care of it. There's
- also no need to save any registers. Finally, the patch glue ensures
- that you can't write a tail patch; if you really want to do so,
- you'll have to modify this library.
-
- (Comment written 93/11/24)
-
- The patch routine must be declared:
-
- pascal <return_type> <function_name> ( PatchType *patch[,...])
-
- For instance, to patch ReadDateTime, declare a function:
-
- pascal OSErr PatchReadDateTime(PatchType *patch, long *secs)
- {
- ... do patch ...
- return(noErr);
- }
-
- then call PatchBegin to install the patch:
-
- PatchType *p;
- p = PatchBegin(PatchReadDateTime, _ReadDateTime, sizeof(long *),
- sizeof(OSErr), data)
-
- The parameters to PatchBegin are:
-
- addr address of patch routine;
- num number of trap to be patched;
- argsize size, in bytes, of arguments to patch (not including the first
- PatchType * patch;
- retsize size, in bytes, of value returned from patch;
- data application defined data, may be accessed from patch->data
- in the patch routine;
-
- For argsize and retsize, remember that the stack must always be
- word-alligned. This means that bytes are pushed on the stack
- as words, so a char value really occupies 2 bytes. You can use
- the typedefs ParameterBooleanParameter, ParameterByteParameter,
- and ParameterCharParameter to get the size of byte sized
- parameters and return values.
-
- You can temporarily remove the patch with PatchRemove and reinstall it with
- PatchInstall. To permanently remove the patch and dispose of the memory it
- occupies call PatchEnd.
-
- In some situations you may want to prevent the original trap routine from
- executing. To do so, set the flag patch->skip to true in your patch routine.
- The flag is reset after your patch routine returns. Normally, the return
- value from a patch routine is ignored, since the original trap routine
- returns a value which overwrites the value returned by the patch routine.
- However, if patch->skip is true, then the value returned by the patch
- routine is returned as the result of the trap. */
-
- /* Revision History:
-
- 94/01/14 aih
- - added typedefs for Boolean, char, and Byte parameters
-
- 93/12/22 aih
- - uses NewPtrClear/DisposPtr instead of calling MemoryLib functions,
- so that we don't need to depend on or intialize MemoryLib
-
- 93/11/22 aih
- - totally rewrote so it no longer uses a self-modifying code resource
- to save the original address of the trap, instead the code and data
- about a patch are stored in a non-relocatable block
-
- 93/11/20 aih
- - modified so patch can prevent execution of original trap
-
- 93/03/26 AIH
- - Changed PatchType to void* instead of pointer to function returning
- void
-
- 91/05/15 AIH
- - Added a few comments
-
- 91/04/19 AIH
- - The trap type is determined at run-time, so there's no need to pass
- the trap type as a parameter to the patch install function
-
- 91/03/23 AIH
- - StripAddress is called before calling NSetTrapAddress. This probably
- is not needed, but TN#213 recommends calling StripAddress before patching
- traps (ok, only for traps that start at a block, if I read it correctly),
- but the issue is so confusing it seems simplest just to call StripAddress.
-
- 91/02/28 AIH
- - Added default definitions for parameter declarations
-
- 91/01/18 Ari Halberstadt (AIH)
- - Wrote this generic patch handling function/macro combination. */
-
- #include "PatchLib.h"
-
- /* list of patches */
- static PatchType *gPatches;
-
- static void _patch_code_bounds(void);
-
- static pascal void _patch_code(void)
- {
- #define patch(x) PatchType.x(patchreg)
- #define patchreg a2 // register containing pointer to patch data
- #define globreg a5 // register containing base of global variables
- #define argszreg d3 // register containing size of arguments
- #define retszreg d4 // register containing size of return value
- #define rsvoffset 4 // offset from a6 to reserved memory
- #define argoffset 12 // offset from a6 to last arg
- #define saveregs a0-a4/d0-d7 // registers saved/restored with movem
-
- asm {
-
- start:
- clr.l -(sp) ; reserve space for trap address
- link a6, #0 ; create stack frame
- movem.l saveregs, -(sp) ; save registers
- jsr @data ; get patch data
- jsr @call ; execute patch
- tst.b patch(skip) ; check if should skip routine
- bne.s @skip
- movem.l (sp)+, saveregs ; restore registers
- unlk a6 ; pop stack frame
- rts ; execute original routine
-
- ; skip patch
- skip:
- clr.b patch(skip) ; clear flag
- move.l argszreg, rsvoffset(a6) ; save size of args
- movem.l (sp)+, saveregs ; restore registers
- unlk a6 ; pop stack frame
- move.l (sp)+, d0 ; pop args size
- move.l (sp)+, a0 ; pop return address
- add.l d0, sp ; pop args
- jmp (a0) ; return to caller
-
- ; get pointer to patch data
- data:
- lea @start, patchreg ; get pointer past patch data
- move.w #sizeof(PatchType), d0 ; get size of patch data
- ext.l d0
- sub.l d0, patchreg ; offset to start of patch data
- move.l patch(argsize), argszreg ; get size of arguments
- move.l patch(retsize), retszreg ; get size of return value
- move.l patch(trap), rsvoffset(a6) ; set return address
- rts
-
- ; call patch routine
- call:
- move.l globreg, -(sp) ; setup global base register
- move.l patch(globalreg), globreg
- sub.l retszreg, sp ; reserve space for return value
- move.l patchreg, -(sp) ; push patch data
- sub.l argszreg, sp ; reserve space for args
- move.l argszreg, d0 ; copy args
- lea argoffset(a6), a0
- move.l sp, a1
- BlockMove
- move.l patch(addr), a0 ; call patch
- jsr (a0)
- move.l sp, a0 ; copy return value
- lea argoffset(a6), a1
- add.l argszreg, a1
- move.l retszreg, d0
- BlockMove
- add.l retszreg, sp ; pop return value
- move.l (sp)+, globreg ; restore global base register
- rts
- end:
-
- ; call to get bounds of function
- extern _patch_code_bounds:
- lea @start, a0
- lea @end, a1
- rts
- }
- }
-
- /* insert patch into list of patches, return head of list */
- static PatchType *PatchInsert(PatchType *list, PatchType *patch)
- {
- patch->next = list;
- return(patch);
- }
-
- /* remove patch from list of patches, return head of list */
- static PatchType *PatchDelete(PatchType *list, PatchType *patch)
- {
- PatchType *p = NULL;
- PatchType *prev = NULL;
-
- for (p = list; p && p != patch; p = p->next)
- prev = p;
- if (p) {
- if (prev)
- prev->next = p->next;
- else
- list = p->next;
- }
- return(list);
- }
-
- /* Patch to ExitToShell to remove our patches before exiting. It would
- be possible to call a function PatchRemoveAll before exiting,
- but in the case of an abnormal termination (e.g., some fatal error)
- we must still remove the patches. */
- static pascal void PatchExitToShell(PatchType *patch)
- {
- PatchType *p = NULL;
-
- /* We only remove the patch, but don't dispose of the memory it
- occupies, since that would dispose of the code that called
- this routine. Since the application is terminating the entire
- heap is going to be disposed of anyway. */
- for (p = gPatches; p; p = p->next)
- PatchRemove(p);
- }
-
- /* install the patch */
- void PatchInstall(PatchType *patch)
- {
- if (! patch->installed) {
- NSetTrapAddress((long) ((char *) patch + sizeof(PatchType)), patch->num, patch->type);
- patch->installed = true;
- }
- }
-
- /* remove the patch */
- void PatchRemove(PatchType *patch)
- {
- if (patch->installed) {
- NSetTrapAddress((long) patch->trap, patch->num, patch->type);
- patch->installed = false;
- }
- }
-
- /* create a patch */
- PatchType *PatchBegin(void *addr, short num, long argsize, long retsize, void *data)
- {
- char *p, *q;
- void *trap = NULL;
- long globalreg = 0;
- PatchType *patch = NULL;
- static Boolean initialized;
-
- require(argsize >= 0 && (argsize & 1) == 0);
- require(retsize >= 0 && (retsize & 1) == 0);
- if (! initialized) {
- initialized = true;
- (void) PatchBegin(PatchExitToShell, _ExitToShell, 0, 0, NULL);
- }
- _patch_code_bounds();
- asm {
- move.l a0, p
- move.l a1, q
- }
- patch = (PatchType *) NewPtrClear(sizeof(PatchType) + q - p);
- BlockMove(p, (char *) patch + sizeof(PatchType), q - p);
- asm { move.l globreg, globalreg }
- patch->num = num;
- patch->data = data;
- patch->argsize = argsize;
- patch->retsize = retsize;
- patch->globalreg = globalreg;
- patch->type = TrapTypeGet(patch->num);
- patch->addr = (void *) StripAddress(addr);
- patch->trap = (void *) NGetTrapAddress(patch->num, patch->type);
- gPatches = PatchInsert(gPatches, patch);
- PatchInstall(patch);
- return(patch);
- }
-
- /* remove and dispose of a patch */
- void PatchEnd(PatchType *patch)
- {
- if (patch) {
- gPatches = PatchDelete(gPatches, patch);
- PatchRemove(patch);
- DisposPtr((Ptr) patch);
- }
- }
-